from typing import Annotated

import secrets

from fastapi import Response, Depends
from app.models import responses
from app.models.accounts import AccountLoginJSON, ModifyPasswordJSON
from app.models.cookies import CookieDep
from app.database.postgre import SessionDep, select, col
from app.database.redis import get_auth_session_redis
from app.database.models import Account, App
from app.utils.password import verify_password, get_password_hash
from app.utils.time import get_now_date
from app.utils.uuid import generate_session_id
from app.config import get_settings


async def handle_client_login(json_data: AccountLoginJSON, session: SessionDep):
    """处理客户端登录"""
    username = json_data.username
    password = json_data.password

    account = session.exec(select(Account).where(Account.username == username)).first()

    if account is None:
        raise responses.FailureResponseError(-204)

    if not verify_password(password, account.password):
        raise responses.FailureResponseError(-204)

    if account.is_banned:
        raise responses.FailureResponseError(-203)

    account.login_time = get_now_date()
    session.commit()
    session.refresh(account)
    return account


async def handle_login_success(account: Account, response: Response):
    """处理登录成功后的事务"""
    login_time = int(account.login_time.timestamp())
    expire_seconds = get_settings().SESSION_EXPIRE_MINUTES * 60
    account_id = account.aid
    session_id = generate_session_id(account_id, login_time, expire_seconds)

    auth_session_redis = get_auth_session_redis()
    await auth_session_redis.setex(session_id, expire_seconds, account_id)

    response.set_cookie(key='sid', value=session_id, httponly=True, samesite="strict", expires=expire_seconds)
    response.set_cookie(key='aid', value=account_id, httponly=True, samesite="strict", expires=expire_seconds)
    response.set_cookie(key='ts', value=login_time, samesite='strict', expires=expire_seconds)

    response_data = account.model_dump(include={'username'})
    response_data['session_id'] = session_id
    response_data['expires'] = expire_seconds

    return responses.SuccessResponse(
        msg='登录成功',
        data=response_data
    )


async def verify_client_session(cookie: CookieDep, session: SessionDep):
    """验证客户端 Cookie 中的 session id"""
    auth_session_redis = get_auth_session_redis()
    account_id = await auth_session_redis.get(cookie.sid)
    if account_id is None or cookie.aid != account_id:
        raise responses.FailureResponseError(-201)

    # 获取账号
    account = Account.get_by_aid(session, account_id)
    if not account:
        raise responses.FailureResponseError(-201)

    # 检查账号是否被封禁
    if account.is_banned:
        raise responses.FailureResponseError(-203)

    # 检查登录时间是否与对应账号的登录时间一致（挤号登录）
    login_time = cookie.ts or 0
    if login_time != int(account.login_time.timestamp()):
        raise responses.FailureResponseError(-207)

    return account


# 已登录账号
LoggedInAccount = Annotated[Account, Depends(verify_client_session)]


async def handle_client_logout(account: Account, cookie: CookieDep, response: Response):
    """处理客户端的退出登录"""
    auth_session_redis = get_auth_session_redis()
    await auth_session_redis.delete(cookie.sid)
    response.set_cookie(key='sid', value='', httponly=True, samesite="strict", max_age=0)
    return responses.SuccessResponse(msg='已退出登录', data=account.model_dump(include={'aid'}))


async def modify_password_by_old(account: Account, json_data: ModifyPasswordJSON, session: SessionDep):
    """通过旧密码修改密码"""
    account_id = json_data.account_id
    old_password = json_data.old_password
    new_password = json_data.new_password

    # 校验修改密码的账号是否一致
    if not secrets.compare_digest(account_id.encode('utf-8'), account.aid.encode('utf-8')):
        return responses.Response205()

    # 校验旧密码是否一致
    if not verify_password(old_password, account.password):
        return responses.Response204()

    # 修改密码，修改登录时间（踢出登录）
    account.password = get_password_hash(new_password)
    account.login_time = get_now_date()
    session.commit()
    session.refresh(account)
    return responses.SuccessResponse(msg='密码已修改，请重新登录')


async def get_my_info(account: Account):
    """获取个人信息"""
    response_data = account.model_dump(include={'aid', 'username', 'avatar', 'email', 'max_apps', 'identity'})
    response_data['roles'] = account.roles
    return responses.SuccessResponse(data=response_data)


async def get_my_simple_apps(account: Account, session: SessionDep):
    """获取个人所有应用的简要"""
    stmt = (select(App.id, App.name)
            .where(
                App.account_id == account.aid
            )
            .order_by(col(App.create_time).desc()))

    result = session.exec(stmt)

    simple_apps = [
        {"id": app.id, "name": app.name}
        for app in result.all()
    ]

    return responses.SuccessResponse(data={
        'apps': simple_apps
    })
